package com.ciaojian.core.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ciaojian.core.constant.RedisConstant;
import com.ciaojian.core.enums.business.CouponCodeOriginEnum;
import com.ciaojian.core.enums.business.CouponCodeTakeRecordTypeEnum;
import com.ciaojian.core.mapper.CouponCodeMapper;
import com.ciaojian.core.mapper.CouponMapper;
import com.ciaojian.core.mapper.ShareRecordMapper;
import com.ciaojian.core.model.*;
import com.ciaojian.core.util.LogUtil;
import com.ciaojian.core.util.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author Atlakyn
 */
@Slf4j
public abstract class CouponCodeService<M extends BaseMapper<T>, T> extends ServiceImpl<CouponCodeMapper, CouponCode> {
    @Resource
    private CouponService<CouponMapper, Coupon> couponService;
    @Resource
    private ShareRecordService<ShareRecordMapper, ShareRecord> shareRecordService;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;


    /**
     * 取一张未被领取的券
     *
     * @param couponId 券类型
     * @return 如果Redis存入成功返回券, 否则返回 null
     */
    public CouponCode getStock(Integer couponId) {
        // 券码
        CouponCode codeStock = this.lambdaQuery()
                .eq(CouponCode::getCouponId, couponId)
                .eq(CouponCode::getStatus, 0)
                .last("limit 1").one();

        // 用 setIfAbsent() 防止对同一张券重复领取
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(
                RedisConstant.COUPON_CODE.concat(String.valueOf(codeStock.getId())),
                codeStock,
                3,
                TimeUnit.MINUTES);
        return Boolean.TRUE.equals(flag) ? codeStock : null;
    }

    /**
     * 删除缓存里的券码
     */
    public void deleteCodeInCache(Integer couponCodeId) {
        redisTemplate.delete(RedisConstant.COUPON_CODE.concat(String.valueOf(couponCodeId)));
    }


    /**
     * 裂变领券
     *
     * @param activityId 活动id
     */
    public void takeCodeMS(Integer inviterId, Integer inviterCouponId, Integer activityId, Integer shareId, Integer shareCouponId, CouponCodeTakeRecordTypeEnum type) {
        //裂变活动的邀请人可重复领券,判断同一张券用户是否领取过
        Assert.isTrue(this.checkNotTook(shareId, shareCouponId, activityId), "您已经领取");

        takes(inviterId, inviterCouponId, activityId, CouponCodeTakeRecordTypeEnum.MARKETING_SHARE);
        takes(shareId, shareCouponId, activityId, CouponCodeTakeRecordTypeEnum.MARKETING_SHARE);

        ShareRecord shareRecord = new ShareRecord();
        shareRecord.setInviteId(inviterId);
        shareRecord.setShareId(shareId);
        shareRecord.setMId(activityId);
        shareRecordService.save(shareRecord);
    }

    /**
     * 根据用户id发券
     *
     * @param userId
     * @param couponId
     * @param mid
     * @param type
     */
    public void takes(Integer userId, Integer couponId, Integer mid, CouponCodeTakeRecordTypeEnum type) {
        // 给用户新增券
        QueryWrapper<CouponCode> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("coupon_id", couponId)
                .eq("status", 0)
                .isNull("user_id")
                .last("limit 1");
        CouponCode couponCode = new CouponCode().selectOne(queryWrapper);
        Assert.notNull(couponCode, "券码已被抢完");


        couponCode.setStatus(1);
        couponCode.setUserId(userId);
        couponCode.setOrigin(CouponCodeOriginEnum.pop_collect);
        couponCode.setExpiredTime(couponService.getCouponExpiredTime(couponId));
        couponCode.updateById();

        // 新增领取记录
        CouponCodeTakeRecord couponCodeTakeRecord = new CouponCodeTakeRecord();
        couponCodeTakeRecord.setUserId(userId);
        couponCodeTakeRecord.setCouponId(couponId);
        couponCodeTakeRecord.setCouponCode(couponCode.getCode());
        couponCodeTakeRecord.setActivityId(mid);
        couponCodeTakeRecord.setType(type);
        couponCodeTakeRecord.insert();
        // 领取数量 + 1
        couponService.addGetNum(couponId);
    }


    /**
     * 判断同一个活动用户是否领取过
     *
     * @param userId     用户id
     * @param couponId   券类型id
     * @param activityId 活动id
     * @return 领取过返回 true
     */
    public abstract Boolean checkNotTook(Integer userId, Integer couponId, Integer activityId);

    /**
     * 给用户发放单张券
     *
     * @param couponId 券类型
     * @param userId   用户
     * @param origin   来源
     * @return 发放的券
     */
    public CouponCode giveSingleCouponCode(
            Integer couponId,
            Integer userId,
            CouponCodeOriginEnum origin) {
        return this.giveSingleCouponCode(couponId,
                userId,
                couponService.getBaseMapper().getCouponExpiredTimeById(couponId),
                origin);
    }

    /**
     * 给用户发放单张券
     *
     * @param couponId          券类型
     * @param userId            用户
     * @param couponExpiredTime 券码过期时间
     * @param origin            来源
     * @return 发放的券
     */
    public CouponCode giveSingleCouponCode(
            Integer couponId,
            Integer userId,
            Date couponExpiredTime,
            CouponCodeOriginEnum origin) {
        // 取一张券
        CouponCode stock = this.getStock(couponId);
        // 如果为空表示被别人先锁了,先退出程序
        Assert.notNull(stock, "网络开小差了,稍后再试试吧~");

        stock.setUserId(userId);
        stock.setOrigin(origin);
        stock.setStatus(1);
        stock.setExpiredTime(couponExpiredTime);
        // 券数量+1
        couponService.addGetNum(couponId);
        this.deleteCodeInCache(stock.getId());
        return stock;
    }

    /**
     * 给用户发放单张券
     *
     * @param couponId          券类型
     * @param userId            用户
     * @param origin            来源
     * @return 发放的券
     */
    public Boolean giveSingleCouponCodeV2(
            Integer couponId,
            Integer userId,
            CouponCodeOriginEnum origin) {
        Date expiredTime = couponService.getCouponExpiredTime(couponId);
        return baseMapper.giveSingleCouponCode(userId, origin, expiredTime, couponId);
        // TODO 消除缓存
    }

    /**
     * 根据券包给用户发券
     *
     * @param userId          用户ID
     * @param order           订单
     * @param couponPackageId 券包ID
     * @param origin          来源
     */
    public void giveCouponCodeByCouponPackage(Integer userId,
                                              Order order,
                                              Integer couponPackageId,
                                              CouponCodeOriginEnum origin) {
        CouponPackage couponPackage = new CouponPackage().selectById(couponPackageId);
        if (couponPackage == null) {
            LogUtil.writeBusinessLog("券包不存在!");
            return;
        }
        for (String couponId : couponPackage.getCoupons().split(",")) {
            // 券码id
            Integer cId = Integer.valueOf(couponId);
            // 取一张券
            CouponCode stock = this.giveSingleCouponCode(cId, userId, couponService.getCouponExpiredTime(cId), origin);
            if (order != null) {
                stock.setOrderId(order.getId());
            }
            boolean flag = stock.updateById();
            if (!flag) {
                System.err.println("修改失败");
            }

            // 新增领取记录
            CouponCodeTakeRecord couponCodeTakeRecord = new CouponCodeTakeRecord();
            couponCodeTakeRecord.setUserId(userId);
            if (order != null && order.getWorkerId() != null) {
                couponCodeTakeRecord.setWorkerId(order.getWorkerId());
            }
            if (order != null && order.getStoreId() != null) {
                couponCodeTakeRecord.setStoreId(order.getStoreId());
            }
            couponCodeTakeRecord.setOrigin(origin);
            couponCodeTakeRecord.setCouponId(cId);
            couponCodeTakeRecord.setCouponCode(stock.getCode());
            couponCodeTakeRecord.insert();
            // 券数量+1
            couponService.addGetNumByCouponPackageId(couponPackageId);
        }
    }

    /**
     * 根据权益给用户发券
     *
     * @param userId       用户
     * @param order        订单
     * @param memberDetail 券包
     * @param origin       优惠券来源
     */
    public void giveCouponCodeByMemberDetail(Integer userId,
                                             Order order,
                                             MemberDetail memberDetail,
                                             CouponCodeOriginEnum origin) {
        CouponPackage couponPackage = new CouponPackage().selectById(memberDetail.getCouponId());
        if (couponPackage == null) {
            LogUtil.writeBusinessLog("券包不存在!");
            return;
        }
        for (String couponId : couponPackage.getCoupons().split(",")) {
            try {
                log.info("权益给用户发券userId:" + userId + ",orderId:" + order.getId() + ",couponId:" + couponId);
                // 券码id
                Integer cId = Integer.valueOf(couponId);
                // 取一张券
                CouponCode stock = this.giveSingleCouponCode(cId, userId, couponService.getCouponExpiredTime(cId), origin);
                if (order != null) {
                    stock.setOrderId(order.getId());
                }
                boolean flag = stock.updateById();
                if (!flag) {
                    System.err.println("修改失败");
                }

                // 插入券码权益关联记录
                new CouponCodeMemberDetail(stock.getId(), memberDetail.getId()).insert();

                // 新增领取记录
                CouponCodeTakeRecord couponCodeTakeRecord = new CouponCodeTakeRecord();
                couponCodeTakeRecord.setUserId(userId);
                if (order != null && order.getWorkerId() != null) {
                    couponCodeTakeRecord.setWorkerId(order.getWorkerId());
                }
                if (order != null && order.getStoreId() != null) {
                    couponCodeTakeRecord.setStoreId(order.getStoreId());
                }
                couponCodeTakeRecord.setOrigin(origin);
                couponCodeTakeRecord.setCouponId(cId);
                couponCodeTakeRecord.setCouponCode(stock.getCode());
                couponCodeTakeRecord.insert();
                // 券数量+1
                couponService.addGetNumByCouponPackageId(memberDetail.getCouponId());
            }catch (Exception e){
                log.info("权益给用户发券失败userId:" + userId + ",orderId:" + order.getId() + ",couponId:" + couponId);
            }
        }
    }


}
